﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using SilverlightClientLibrary.Messages;

namespace HostLibrary.Hosts
{
    public class TcpListenerController
    {
        private TcpListener _listener;
        private List<TcpClientController> _clients;
        private Thread _listenerThread;
        private Thread _workerThread;
        private int _bufferSize;

        public TcpListenerController(int portNumber, int bufferSize)
        {
            _clients = new List<TcpClientController>();
            _workerThread = new Thread(ClientsReadWrite);
            _listener = new TcpListener(IPAddress.Any, portNumber);
            _listenerThread = new Thread(ListenForClients);
            _bufferSize = bufferSize;
        }

        public void Start()
        {
            _workerThread.Start();
            _listenerThread.Start();
        }

        public void Stop()
        {
            _workerThread.Abort();
            _listenerThread.Abort();
        }

        private void ListenForClients()
        {
            _listener.Start();

            while (true)
            {
                TcpClient client = _listener.AcceptTcpClient();
                if (client != null)
                {
                    try
                    {
                        lock (_clients)
                        {
                            byte lowest = 1;
                            _clients = _clients.OrderBy(x => x.Id).ToList();
                            foreach (TcpClientController tcc in _clients)
                            {
                                if (tcc.Id == lowest)
                                    lowest++;
                            }
                            NetworkStream ns = client.GetStream();
                            for (int i = 0; i < _clients.Count; i++)
                            {
                                NewClientMessage ncm = new NewClientMessage(lowest, _bufferSize);
                                byte[] buffer = ncm.Encode();
                                NetworkStream s = _clients[i].Stream;
                                s.Write(buffer, 0, buffer.Length);

                                ncm = new NewClientMessage(_clients[i].Id, _bufferSize);
                                buffer = ncm.Encode();
                                ns.Write(buffer, 0, buffer.Length);
                            }
                            _clients.Add(new TcpClientController(client, _clients, (byte)lowest));
                        }
                    }
                    catch (Exception exc)
                    {
                        Console.WriteLine(exc.ToString());
                    }
                }
            }
        }

        private void ClientsReadWriteAsync()
        {
            while (true)
            {
                lock (_clients)
                {
                    //this line randomly drops clients, so we're leaving it out
                    //_clients = _clients.OrderByDescending(x => x.AvailableBytesToRead).ToList();
                    for (byte i = 0; i < _clients.Count; i++)
                    {
                        TcpClientController client = _clients[i];
                        if (client.Client.Connected)
                        {
                            if(client.AvailableBytesToRead > 0)
                            {
                                client.UpdateClients(_clients);
                                client.ClientReadAsync(_bufferSize);
                            }
                        }
                        else
                        {
                            ClientRemovedMessage crm = new ClientRemovedMessage(client.Id);
                            byte[] buffer = crm.Encode();
                            for (int j = 0; j < _clients.Count; j++)
                            {
                                if (i != j)
                                {
                                    try
                                    {
                                        NetworkStream s = _clients[j].Stream;
                                        s.Write(buffer, 0, buffer.Length);
                                    }
                                    catch (Exception exc)
                                    {
                                        Console.WriteLine(exc.ToString());
                                    }
                                }
                            }
                            _clients.RemoveAt(i);
                            i--;
                        }
                    }
                }
            }
        }

        private void RemoveClient()
        {
            //ClientRemovedMessage crm = new ClientRemovedMessage(i);
            //byte[] buffer = crm.Encode();
            //for (int j = 0; j < clients.Count; j++)
            //{
            //    if (i != j)
            //    {
            //        try
            //        {
            //            NetworkStream s = networkStreams[j];
            //            if (s.CanWrite)
            //                s.Write(buffer, 0, buffer.Length);
            //        }
            //        catch (Exception exc)
            //        {
            //            Console.WriteLine(exc.ToString());
            //        }
            //    }
            //}
            //clients.RemoveAt(i);
            //networkStreams.RemoveAt(i);
        }

        private void ClientsReadWrite()
        {
            byte[] readBuffer = new byte[_bufferSize];

            while (true)
            {
                lock (_clients)
                {
                    //this line randomly drops clients, so we're leaving it out
                    //_clients = _clients.OrderByDescending(x => x.AvailableBytesToRead).ToList();
                    for (byte i = 0; i < _clients.Count; i++)
                    {
                        TcpClientController client = _clients[i];
                        if (client.Client.Connected)
                        {
                            if (client.AvailableBytesToRead > 0)
                            {
                                try
                                {
                                    int bytesRead = 0;
                                    int lastBytes = 1;
                                    while (bytesRead < _bufferSize)
                                    {
                                        lastBytes = client.Stream.Read(readBuffer, bytesRead, readBuffer.Length - bytesRead);
                                        bytesRead += lastBytes;
                                    }
                                    if (bytesRead == _bufferSize)
                                    {
                                        readBuffer[1] = client.Id;
                                        foreach (TcpClientController tcc in _clients)
                                        {
                                            if (tcc.Id != client.Id)
                                            {
                                                tcc.Stream.Write(readBuffer, 0, readBuffer.Length);
                                            }
                                        }
                                    }
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(e.ToString());
                                }
                            }
                        }
                        else
                        {
                            ClientRemovedMessage crm = new ClientRemovedMessage(client.Id);
                            byte[] errorBuffer = crm.Encode();
                            for (int j = 0; j < _clients.Count; j++)
                            {
                                if (i != j)
                                {
                                    try
                                    {
                                        NetworkStream s = _clients[j].Stream;
                                        s.Write(errorBuffer, 0, errorBuffer.Length);
                                    }
                                    catch (Exception exc)
                                    {
                                        Console.WriteLine(exc.ToString());
                                    }
                                }
                            }
                            _clients.RemoveAt(i);
                            i--;
                        }
                    }
                }
            }
        }
    }
}
